package com.kpmg.datalake.common.utils;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.crypto.Cipher;
import org.apache.commons.codec.binary.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class RSAUtil {

  private static final Logger logger = LoggerFactory.getLogger(RSAUtil.class);
  private static String KEY_RSA_TYPE = "RSA";
  //JDK方式RSA加密最大只有1024位
  private static int KEY_SIZE = 1024;
  private static int ENCODE_PART_SIZE = KEY_SIZE / 8;
  public static final String PUBLIC_KEY_NAME = "public";
  public static final String PRIVATE_KEY_NAME = "private";

  /**
   * 创建公钥秘钥
   */
  public static Map<String, String> createRSAKeys() {
    //里面存放公私秘钥的Base64位加密
    Map<String, String> keyPairMap = new HashMap<>();
    try {
      KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_RSA_TYPE);
      keyPairGenerator.initialize(KEY_SIZE, new SecureRandom());
      KeyPair keyPair = keyPairGenerator.generateKeyPair();

      //获取公钥秘钥
//        String publicKeyValue = Base64.encodeBase64String(keyPair.getPublic().getEncoded());
//        String privateKeyValue = Base64.encodeBase64String(keyPair.getPrivate().getEncoded());
      String publicKeyValue = Hex.encodeHexString(keyPair.getPublic().getEncoded());
      String privateKeyValue = Hex.encodeHexString(keyPair.getPrivate().getEncoded());
      //存入公钥秘钥，以便以后获取
      keyPairMap.put(PUBLIC_KEY_NAME, publicKeyValue);
      keyPairMap.put(PRIVATE_KEY_NAME, privateKeyValue);
    } catch (NoSuchAlgorithmException e) {
      logger.error("当前JDK版本没找到RSA加密算法！", e.getMessage());
    }
    return keyPairMap;
  }

  /**
   * 公钥加密 描述： 1字节 = 8位； 最大加密长度如 1024位私钥时，最大加密长度为 128-11 = 117字节，不管多长数据，加密出来都是 128 字节长度。
   */
  public static String encode(String sourceStr, String publicKeyBase64Str) {

    String encodeBase64Result = null;
    try {
      //      byte [] publicBytes = Base64.decodeBase64(publicKeyBase64Str);
      byte[] publicBytes = Hex.decodeHex(publicKeyBase64Str.toCharArray());
      //公钥加密
      X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicBytes);
      List<byte[]> alreadyEncodeListData = new LinkedList<>();

      int maxEncodeSize = ENCODE_PART_SIZE - 11;
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA_TYPE);
      PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
      Cipher cipher = Cipher.getInstance(KEY_RSA_TYPE);
      cipher.init(Cipher.ENCRYPT_MODE, publicKey);
      byte[] sourceBytes = sourceStr.getBytes(UTF_8);
      int sourceLen = sourceBytes.length;
      for (int i = 0; i < sourceLen; i += maxEncodeSize) {
        int curPosition = sourceLen - i;
        int tempLen = curPosition;
        if (curPosition > maxEncodeSize) {
          tempLen = maxEncodeSize;
        }
        //待加密分段数据
        byte[] tempBytes = new byte[tempLen];
        System.arraycopy(sourceBytes, i, tempBytes, 0, tempLen);
        byte[] tempAlreadyEncodeData = cipher.doFinal(tempBytes);
        alreadyEncodeListData.add(tempAlreadyEncodeData);
      }
      //加密次数
      int partLen = alreadyEncodeListData.size();
      int allEncodeLen = partLen * ENCODE_PART_SIZE;
      //存放所有RSA分段加密数据
      byte[] encodeData = new byte[allEncodeLen];
      for (int i = 0; i < partLen; i++) {
        byte[] tempByteList = alreadyEncodeListData.get(i);
        System.arraycopy(tempByteList, 0, encodeData, i * ENCODE_PART_SIZE, ENCODE_PART_SIZE);
      }
//        encodeBase64Result = Base64.encodeBase64String(encodeData);
      encodeBase64Result = Hex.encodeHexString(encodeData);
    } catch (Exception e) {
      logger.error("加密失败", e.getMessage());
    }
    return encodeBase64Result;
  }

  /**
   * 私钥解密
   */
  public static String decode(String sourceBase64RSA, String privateKeyBase64Str) {

    String decodeStrResult = null;
    try {
//        byte[] privateBytes = Base64.decodeBase64(privateKeyBase64Str);
//        byte[] encodeSource = Base64.decodeBase64(sourceBase64RSA);
      byte[] privateBytes = Hex.decodeHex(privateKeyBase64Str.toCharArray());
      byte[] encodeSource = Hex.decodeHex(sourceBase64RSA.toCharArray());
      int encodePartLen = encodeSource.length / ENCODE_PART_SIZE;
      //所有解密数据
      List<byte[]> decodeListData = new LinkedList<>();
      //私钥解密
      PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateBytes);
      KeyFactory keyFactory = KeyFactory.getInstance(KEY_RSA_TYPE);
      PrivateKey privateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
      Cipher cipher = Cipher.getInstance(KEY_RSA_TYPE);
      cipher.init(Cipher.DECRYPT_MODE, privateKey);
      //初始化所有被解密数据长度
      int allDecodeByteLen = 0;
      for (int i = 0; i < encodePartLen; i++) {
        byte[] tempEncodedData = new byte[ENCODE_PART_SIZE];
        System.arraycopy(encodeSource, i * ENCODE_PART_SIZE, tempEncodedData, 0, ENCODE_PART_SIZE);
        byte[] decodePartData = cipher.doFinal(tempEncodedData);
        decodeListData.add(decodePartData);
        allDecodeByteLen += decodePartData.length;
      }
      byte[] decodeResultBytes = new byte[allDecodeByteLen];
      for (int i = 0, curPosition = 0; i < encodePartLen; i++) {
        byte[] tempSorceBytes = decodeListData.get(i);
        int tempSourceBytesLen = tempSorceBytes.length;
        System.arraycopy(tempSorceBytes, 0, decodeResultBytes, curPosition, tempSourceBytesLen);
        curPosition += tempSourceBytesLen;
      }
      decodeStrResult = new String(decodeResultBytes, UTF_8);
    } catch (Exception e) {
      logger.error("解密失败", e);
    }
    return decodeStrResult;
  }


  public static void main(String[] args) {

    Map<String, String> rsaKeys = RSAUtil.createRSAKeys();
    String pubKey = rsaKeys.get(PUBLIC_KEY_NAME);
    logger.info("pubKey\r\n" + pubKey);
    String prvKey = rsaKeys.get(PRIVATE_KEY_NAME);
    logger.info("prvKey\r\n" + prvKey);
    String pubStr = RSAUtil.encode("datalake", pubKey);
    logger.info("pubStr\r\n" + pubStr);
    String prvStr = RSAUtil.decode(pubKey, prvKey);
    logger.info("prvStr\r\n" + prvStr);

  }
}


